﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model_Layer;
using Data_Access_Layer;
using System.Windows.Forms;


namespace Business_Logic_Layer
{
    public class BLLDijkstra
    {
        //List<Airport> dijkstraSolutionList;
        #region RunDijkstra method
        public List<Airport> RunDijkstra(Airport source, Airport target)
        {
            try
            {
                DBFlight flightDB = new DBFlight();
                List<Flight> edgoObjList = flightDB.GetAllFlights(true);

                DBAirport airportDB = new DBAirport();
                List<Airport> verticeObjList = airportDB.GetAllAirports();

                List<Airport> dijkstraSolutionList = new List<Airport>();
                Airport u = source;
                #region Source.Dist to 0 and infinite on all other Airport vertices
                //Airport nullAirport = new Airport();
                //nullAirport.Name = "null";
                foreach (Airport v in verticeObjList)
                {
                    if (v.IdAirport == u.IdAirport)
                    {
                        v.Dist = 0; // Source airport
                    }
                    else
                    {
                        v.Dist = int.MaxValue; //set every other airportdistance in the list to infinity.
                        v.PreviousVertex = null; // undefined
                    }
                        //v.PreviousVertex = nullAirport; // for being able to print within the method.

                    //v.FlightList = new List<Flight>();
                    v.AdjFlight = new Flight();
                    v.Price = 0.0;
                }
                #endregion
                //List<Airport> myQueue = airportlist;

                while (verticeObjList.Count > 0)
                {
                    u = SmallestDistInQ(verticeObjList);
                    verticeObjList.Remove(u);
                    dijkstraSolutionList.Add(u);

                    if (u.Dist == int.MaxValue)
                    {
                        break;
                    }

                    //Flight adjFlight = AdjFlight(edgoObjList, u);
                    List<Flight> adjFlightList = AdjFlights(edgoObjList, u);

                    #region Relaxation step
                    int alt;
                    for (int i = 0; i < adjFlightList.Count - 1; i++)
                    {
                        for (int j = 0; j < verticeObjList.Count; ++j)
                        {
                            if (verticeObjList[j].IdAirport == adjFlightList[i].DestinationAirport.IdAirport)
                            {
                                alt = u.Dist + adjFlightList[i].Traveltime;
                                if (alt < verticeObjList[j].Dist)
                                {
                                    verticeObjList[j].Dist = alt;
                                    verticeObjList[j].PreviousVertex = u;
                                    verticeObjList[j].AdjFlight = adjFlightList[i];
                                    verticeObjList[j].Price = adjFlightList[i].Price;

                                    #region Test method for relaxation step
                                    //MessageBox.Show("Current v: " + verticeObjList[j].Name.ToString() + "\n" +
                                    //    "Dist: " + verticeObjList[j].Dist.ToString() + "\n" +
                                    //    "Airport ID: " + verticeObjList[j].IdAirport.ToString() + "\n" +
                                    //    "Previous vertex: " + verticeObjList[j].PreviousVertex.Name.ToString());
                                    #endregion
                                }
                                #region Extra functionallity for optimization
                                //else if (alt == verticeObjList[j].Dist)
                                //{
                                //    verticeObjList[j].Dist = alt;
                                //    verticeObjList[j].PreviousVertex = u;
                                //    verticeObjList[j].FlightList.Clear();
                                //    verticeObjList[j].FlightList.Add(adjFlightList[i]);
                                //}
                                #endregion
                            }
                        }
                    }
                    #endregion
                } // while loop

                #region Test for total distance which is to be moved.
                //int totalDist = 0;
                //foreach(Airport a in dijkstraSolutionList)
                //{
                //    totalDist += a.Dist; 
                //}
                //MessageBox.Show("Num of elements in solution list: " + dijkstraSolutionList.Count.ToString() + "\n" +
                //    "Total traveltime: " + totalDist);
                #endregion

                #region Test method for the returned list.
                //foreach (Airport a in dijkstraSolutionList)
                //{
                //    MessageBox.Show("Vertex: " + a.Name.ToString() + "\n" + "Dist: " + a.Dist.ToString() + "\n" + "Airport ID: " + a.IdAirport.ToString());
                //    + "\n" + "Previous vertex:  " + a.PreviousVertex.Name.ToString());
                //}
                #endregion

                dijkstraSolutionList = BacktrackDijkstra(dijkstraSolutionList, target);
                return dijkstraSolutionList;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region Method for BacktrackDijkstra
        private List<Airport> BacktrackDijkstra(List<Airport> verticeObjList, Airport target)
        {
            List<Airport> solutionList = new List<Airport>();
            //loop thru the list to find the actual target with this id on the solution list, if found, replace.
            foreach (Airport airportobj in verticeObjList)
            {
                if (airportobj.IdAirport == target.IdAirport)
                {
                    //replace -to be able to use target in dijkstra solution list. stop after the target element is found
                    target = airportobj;
                    break;
                }
            }
            //set current target with the one we found in dijkstra list
            Airport currentTarget = target;
            double price = 0.0;
            int totaltraveltime = 0;
            Flight startflight = new Flight();

            // While there is a parent to this Airport
            while (currentTarget.PreviousVertex != null)
            {
                solutionList.Add(currentTarget);  // Add Airport to solution

                totaltraveltime += currentTarget.Dist;
                currentTarget.Dist = totaltraveltime;

                price += currentTarget.Price;
                currentTarget.Price = price;

                startflight = currentTarget.AdjFlight;

                currentTarget = currentTarget.PreviousVertex; // Add parent Airport as new Target 
            }

            //to prevent adding target to the list when the list is empty
            if (solutionList.Count > 0)
            {
                currentTarget.Dist = totaltraveltime;
                currentTarget.Price = price;
                currentTarget.AdjFlight = startflight;
                solutionList.Add(currentTarget);
            }

            return solutionList;
        }
        #endregion

        #region Methods used in Dijkstra
        #region Smallest distance in queue
        private Airport SmallestDistInQ(List<Airport> myQueue)
        {
            Airport airportwithsmallestDist = new Airport();
            airportwithsmallestDist.Dist = int.MaxValue;

            foreach (Airport q in myQueue)
            {
                if (q.Dist < airportwithsmallestDist.Dist) 
                {
                    airportwithsmallestDist = q;
                }
            }

            return airportwithsmallestDist;
        }
        #endregion

        #region Get all adjacent Flights
        private List<Flight> AdjFlights(List<Flight> flightList, Airport u)
        {
            List<Flight> adjFlightList = new List<Flight>();
            //if start airport id in flight list has the same id in airport list, pick the entire flight object
            //and add to flight list(adjFlightList)
            foreach (Flight f in flightList)
            {
                if (u.IdAirport == f.StartAirport.IdAirport)
                {
                    adjFlightList.Add(f);
                }
            }

            return adjFlightList;
        }
        #endregion

        //#region Get single adjacent Flight
        //private Flight AdjFlight(List<Flight> flightList, Airport u)
        //{
        //    Flight adjFlight = new Flight();

        //    foreach (Flight f in flightList)
        //    {
        //        if (u.IdAirport == f.StartAirport.IdAirport)
        //        {
        //            adjFlight = f;
        //            break;
        //        }
        //    }
        //    return adjFlight;
        //}
        //#endregion
        #endregion
    }
}